#include "subgraph.h"

static const Color D_AMBIENT = Color(0., 0., 0.);
static const Color D_DIFFUSE = Color(1., 1., 1.);
static const Color D_SPECULAR = Color(0., 0., 0.);
static const Color D_REFLECT = Color(0., 0., 0.);
static const Color D_TRANSPARENT = Color(0., 0., 0.);
static const Color D_EMIT = Color(0., 0., 0.);
static const Color D_EMISSION = Color(0., 0., 0.);
static const double D_SHINE = 1.;
static const double D_ALPHA = 1.;
static const double D_IOR = 1.;

Subgraph::~Subgraph(){
	clearTranses();
}
void Subgraph::clearTranses(){
	for(std::list<Trans*>::iterator it = transes.begin();
		it != transes.end(); it++)
		delete (*it);
	transes.clear();
}
void Subgraph::addTrans(Trans* t){
	transes.push_back(t);
}

void Trans::addRotation(Vector3 axis, double angle){
	double c = std::cos(angle);
	double ic = 1 - c;
	double s = std::sin(angle);
	axis.normalize();
	Matrix4 r = Matrix4(
					Vector4(c + ic*axis[0]*axis[0], ic*axis[0]*axis[1] - s*axis[2], ic*axis[0]*axis[2] + s*axis[1], 0.),
					Vector4(ic*axis[1]*axis[0] + s*axis[2], c + ic*axis[1]*axis[1], ic*axis[1]*axis[2] - s*axis[0], 0.),
					Vector4(ic*axis[2]*axis[0] - s*axis[1], ic*axis[2]*axis[1] + s*axis[0], c + ic*axis[2]*axis[2], 0.),
					Vector4(0., 0., 0., 1.));
	c = std::cos(-angle);
	ic = 1 - c;
	s = std::sin(-angle);
	Matrix4 ir = Matrix4(
					Vector4(c + ic*axis[0]*axis[0], ic*axis[0]*axis[1] - s*axis[2], ic*axis[0]*axis[2] + s*axis[1], 0.),
					Vector4(ic*axis[1]*axis[0] + s*axis[2], c + ic*axis[1]*axis[1], ic*axis[1]*axis[2] - s*axis[0], 0.),
					Vector4(ic*axis[2]*axis[0] - s*axis[1], ic*axis[2]*axis[1] + s*axis[0], c + ic*axis[2]*axis[2], 0.),
					Vector4(0., 0., 0., 1.));
#ifdef _DEBUG
	if(!(matrix * imatrix).approxEqual(Matrix4::identity(), 0.00001)){
		std::cout << "Matrix Inverse Error, Before rotation\nmatrix:\n" << matrix << "\nimatrix:\n" << imatrix << "\nmatrix * imatrix:\n" << (matrix * imatrix) << std::endl;
	}
#endif
	rotation = rotation * r;
	irotation = ir * irotation;
	matrix = matrix * r;
	imatrix = ir * imatrix;
#ifdef _DEBUG
	if(!(matrix * imatrix).approxEqual(Matrix4::identity(), 0.00001)){
		std::cout << "Matrix Inverse Error, After rotation\nmatrix:\n" << matrix << "\nimatrix:\n" << imatrix << "\nmatrix * imatrix:\n" << (matrix * imatrix) << std::endl;
		std::cout << "r:\n" << r << "\nir:\n" << ir << "\nr * ir:\n" << (r * ir) << std::endl;
	}
#endif
}

void Trans::addScaling(double x, double y, double z){
	Matrix4 s = Matrix4(
					Vector4(x, 0., 0., 0.),
					Vector4(0., y, 0., 0.),
					Vector4(0., 0., z, 0.),
					Vector4(0., 0., 0., 1.));
	Matrix4 is = Matrix4(
					Vector4(1./x, 0., 0., 0.),
					Vector4(0., 1./y, 0., 0.),
					Vector4(0., 0., 1./z, 0.),
					Vector4(0., 0., 0., 1.));
#ifdef _DEBUG
	if(!(matrix * imatrix).approxEqual(Matrix4::identity(), 0.00001)){
		std::cout << "Matrix Inverse Error, Before scaling\nmatrix:\n" << matrix << "\nimatrix:\n" << imatrix << "\nmatrix * imatrix:\n" << (matrix * imatrix) << std::endl;
		std::cout << "s:\n" << s << "\nis:\n" << is << "\ns * is:\n" << (s * is) << std::endl;
	}
#endif
	scaling = scaling * s;
	iscaling = is * iscaling;
	matrix = matrix * s;
	imatrix = is * imatrix;
#ifdef _DEBUG
	if(!(matrix * imatrix).approxEqual(Matrix4::identity(), 0.00001)){
		std::cout << "Matrix Inverse Error, After scaling\nmatrix:\n" << matrix << "\nimatrix:\n" << imatrix << "\nmatrix * imatrix:\n" << (matrix * imatrix) << std::endl;
		std::cout << "s:\n" << s << "\nis:\n" << is << "\ns * is:\n" << (s * is) << std::endl;
	}
#endif
}

void Trans::addTranslation(double x, double y, double z){
	Matrix4 t = Matrix4(
					Vector4(1., 0., 0., x),
					Vector4(0., 1., 0., y),
					Vector4(0., 0., 1., z),
					Vector4(0., 0., 0., 1.));
	Matrix4 it = Matrix4(
					Vector4(1., 0., 0., -x),
					Vector4(0., 1., 0., -y),
					Vector4(0., 0., 1., -z),
					Vector4(0., 0., 0., 1.));
	translation = translation * t;
	itranslation = it * itranslation;
	matrix = matrix * t;
	imatrix = it * imatrix;
}


void Object::draw(){
		GLfloat t = static_cast<GLfloat>(alpha);
		GLfloat amb[] = {ambient[0], ambient[1], ambient[2], t};
		glMaterialfv(GL_FRONT, GL_AMBIENT, amb);
		GLfloat dif[] = {diffuse[0], diffuse[1], diffuse[2], t};
		glMaterialfv(GL_FRONT, GL_DIFFUSE, dif);
		GLfloat spec[] = {specular[0], specular[1], specular[2], t};
		glMaterialfv(GL_FRONT, GL_SPECULAR, spec);
		GLfloat emis[] = {emission[0], emission[1], emission[2], t};
		glMaterialfv(GL_FRONT, GL_EMISSION, emis);
		GLfloat s = static_cast<GLfloat>(shine);
		glMaterialfv(GL_FRONT, GL_SHININESS, &s);
	
		shape->draw();	
}

void Subgraph::buildObjectList(Matrix4& m, Matrix4& im, std::list<MatrixObject>* oList){
	for(std::list<Trans*>::iterator it = transes.begin();
		it != transes.end(); it++){
		(*it)->buildObjectList(m, im, oList);
	}
}

void Trans::buildObjectList(Matrix4& m, Matrix4& im, std::list<MatrixObject>* oList){
	sub->buildObjectList(m * matrix, imatrix * im, oList);
}

void Object::buildObjectList(Matrix4& m, Matrix4& im, std::list<MatrixObject>* oList){
	if(alpha == D_ALPHA)
		oList->push_front(MatrixObject(m, im, this));
	else
		oList->push_back(MatrixObject(m, im, this));
}

Trans::Trans(){
	matrix = Matrix4::identity();
	imatrix = Matrix4::identity();
	rotation = Matrix4::identity();
	irotation = Matrix4::identity();
	translation = Matrix4::identity();
	itranslation = Matrix4::identity();
	scaling = Matrix4::identity();
	iscaling = Matrix4::identity();
	sub = 0;
}
Trans::~Trans(){
	#ifdef _DEBUG
	try{
	#endif
		if(sub != 0 
			&& sub->getNodeType() == Node::OBJECT)
			delete sub;
	
	#ifdef _DEBUG
	}catch (...){
		std::cout << "Error could not free Object" << std::endl;
			__asm int 3;
	}
	#endif
}
void Trans::addTransformation(const Matrix4& m){
	matrix = matrix * m;
	imatrix = imatrix * m.inverse();
}
void Trans::setSub(Node* s){
	sub = s;
}

Object::Object(Object* o){
	shape = o->getShape();
	ambient = o->getAmbient();
	diffuse = o->getDiffuse();
	specular = o->getSpecular();
	reflect = o->getReflect();
	transparent = o->getTransparent();
	emit = o->getEmit();
	emission = o->getEmission();
	shine = o->getShine();
	alpha = o->getAlpha();
	ior = o->getIor();
	textureU = o->getTextureU();
	textureV = o->getTextureV();
	textureName = o->getTextureName();
}

Object::Object(Shape* s){
	shape = s;
	ambient = D_AMBIENT;
	diffuse = D_DIFFUSE;
	specular = D_SPECULAR;
	reflect = D_REFLECT;
	transparent = D_TRANSPARENT;
	emit = D_EMIT;
	emission = D_EMISSION;
	shine = D_SHINE;
	alpha = D_ALPHA;
	ior = D_IOR;
}
Object::Object(){
	shape = 0;
	ambient = D_AMBIENT;
	diffuse = D_DIFFUSE;
	specular = D_SPECULAR;
	reflect = D_REFLECT;
	transparent = D_TRANSPARENT;
	emit = D_EMIT;
	emission = D_EMISSION;
	shine = D_SHINE;
	alpha = D_ALPHA;
	ior = D_IOR;
}

MatrixObject::~MatrixObject(){
	if(!inTree) 
		delete o;
}

